Newark City 4311 data anaysis
# Required libs
library(httr)
library(jsonlite)
library(lubridate)
library(tidyverse)
library(stringr)
library(leaflet)
#install.packages("DT")
library(rpart)
library(DT)
library(forecast)
library(rpart.plot)
library(caret)
Parsing Newark Historic data
urls.hist = c("http://data.ci.newark.nj.us/mk/api/3/action/datastore_search?resource_id=1cebcf44-3b2c-4f60-9d5d-817cb35bf9df&limit=40000",
"http://data.ci.newark.nj.us/mk/api/3/action/datastore_search_sql?sql=SELECT%20*%20from%20%221cebcf44-3b2c-4f60-9d5d-817cb35bf9df%22%20WHERE%20_id%20%3E%2031889",
"http://data.ci.newark.nj.us/mk/api/3/action/datastore_search_sql?sql=SELECT%20*%20from%20%221cebcf44-3b2c-4f60-9d5d-817cb35bf9df%22%20WHERE%20_id%20%3E%2063889",
"http://data.ci.newark.nj.us/mk/api/3/action/datastore_search_sql?sql=SELECT%20*%20from%20%221cebcf44-3b2c-4f60-9d5d-817cb35bf9df%22%20WHERE%20_id%20%3E%2095888")
xhist = data.frame()
for(i in urls.hist ){
raw.result <- GET(url = i)
this.raw.content <- rawToChar(raw.result$content)
this.content <- fromJSON(this.raw.content)
json_file <- lapply(this.content, function(x) {
x[sapply(x, is.null)] <- NA
unlist(x)
})
xhistoric = this.content$result$records
xhistoric = xhistoric[,c("Type Name","Type Id","Street Address" , "Longitude","Request Id","Request Date", "Latitude" , "_id" ,"ID")]
xhist = rbind(xhist,xhistoric)
}
dim(xhist)
[1] 107614 9
Parsing 2015 Data
url = "http://data.ci.newark.nj.us/mk/api/3/action/datastore_search?resource_id=bc8e29eb-3c53-418c-a42a-4dbbaae668c2&limit=60000"
raw.result <- GET(url = url)
this.raw.content <- rawToChar(raw.result$content)
this.content <- fromJSON(this.raw.content)
json_file <- lapply(this.content, function(x) {
x[sapply(x, is.null)] <- NA
unlist(x)
})
x2015 = this.content$result$records
dim(x2015)
[1] 14618 10
Parsing 2018 Data
urls.2018 = c("http://data.ci.newark.nj.us/mk/api/3/action/datastore_search?resource_id=b923c1ad-7246-400b-a593-785f67677b94&limit=68408",
"http://data.ci.newark.nj.us/mk/api/3/action/datastore_search_sql?sql=SELECT%20*%20from%20%22b923c1ad-7246-400b-a593-785f67677b94%22%20WHERE%20_id%20%3E%2031993",
"http://data.ci.newark.nj.us/mk/api/3/action/datastore_search_sql?sql=SELECT%20*%20from%20%22b923c1ad-7246-400b-a593-785f67677b94%22%20WHERE%20_id%20%3E%2066495")
x2018 = data.frame()
for(i in urls.2018 ){
raw.result <- GET(url = i)
this.raw.content <- rawToChar(raw.result$content)
this.content <- fromJSON(this.raw.content)
json_file <- lapply(this.content, function(x) {
x[sapply(x, is.null)] <- NA
unlist(x)
})
xtemp = this.content$result$records
xtemp = xtemp[,c("Complaint","DateCreated","Typename","Location","Lot", "ComplaintID","Department" ,"_id" ,"Block")]
x2018 = rbind(x2018,xtemp)
}
dim(x2018)
[1] 71685 9
names(xhist)
[1] "Type Name" "Type Id" "Street Address" "Longitude" "Request Id" "Request Date" "Latitude"
[8] "_id" "ID"
names(x2015)
[1] "Type Name" "Type Id" "Street Name" "Longitude" "Request Id" "Request Date" "Street Number" "Latitude"
[9] "_id" "ID"
names(x2018)
[1] "Complaint" "DateCreated" "Typename" "Location" "Lot" "ComplaintID" "Department" "_id" "Block"
Fixing the 2015 dataset
x2015$`Street Address` = paste(x2015$`Street Number`,x2015$`Street Name`)
x2015$`Street Name` = NULL
x2015$`Street Number` = NULL
sum(is.na(x2018))
[1] 4075
sum(complete.cases(x2018))
[1] 70259
Assign Blank cells with NA’s for x2015
x2015$`Street Address`[x2015$`Street Address` == " "] = NA
sum(is.na(x2015))
[1] 2070
Assign Blank cells with NA’sfor x2018
xhist[xhist == " "] = NA
sum(is.na(xhist))
[1] 3862
Parse Date
xhist$`Request Date` = as.POSIXct(xhist$`Request Date`)
x2015$`Request Date` = "2015"
x2015$`Request Date` = as.Date(x2015$`Request Date`,format = "%Y")
x2018$DateCreated = as.POSIXct(x2018$DateCreated, format = "%b %d %Y")
Creating new combined dataset for x2015 and xhist
till.2015 = rbind(xhist,x2015)
names(till.2015)[1:3]<-c("Complaint","ComplaintID","Location")
names(till.2015)[6]<-c("DateCreated")
overall4311 Dataset with common attributes
overall4311 = rbind(till.2015[,c(1,3,6)], x2018[,c(1,4,2)])
fillColor = "#FFA07A"
fillColor2 = "#F1C40F"
x2018 %>%
group_by(Typename) %>%
summarise(Count = n()) %>%
ungroup() %>%
mutate(complaint_description = reorder(Typename,Count)) %>%
arrange(desc(Count)) %>%
head(10) %>%
ggplot(aes(x = reorder(Typename,Count),y = Count )) +
geom_bar(stat='identity',colour="white", fill = fillColor2) +
geom_text(aes(x = complaint_description, y = 1, label = paste0("(",Count,")",sep="")),
hjust=0, vjust=.5, size = 4, colour = 'black',
fontface = 'bold') +
labs(x = 'Complaint Descriptors',
y = 'Count',
title = 'Highest Complaint Typename 2018') +
coord_flip() +
theme_bw()

Combing the datasets for getting overall Complaint typename
temp = c(xhist$`Type Name`,x2015$`Type Name`,x2018$Typename)
m1 <- matrix(temp, ncol=1, byrow=TRUE)
overall.complaints <- as.data.frame(m1, stringsAsFactors=FALSE)
overall.complaints
dim(overall.complaints)
[1] 193917 1
overall.complaints %>%
group_by(V1) %>%
summarise(Count = n()) %>%
ungroup() %>%
mutate(complaint_description = reorder(V1,Count)) %>%
arrange(desc(Count)) %>%
head(20) %>%
ggplot(aes(x = reorder(V1,Count),y = Count )) +
geom_bar(stat='identity',colour="white", fill = fillColor2) +
geom_text(aes(x = complaint_description, y = 1, label = paste0("(",Count,")",sep="")),
hjust=0, vjust=.5, size = 4, colour = 'black',
fontface = 'bold') +
labs(x = 'Complaint Descriptors',
y = 'Count',
title = 'Highest Complaint Typename') +
coord_flip() +
theme_bw()

Cluster Analysis of Complaints through Location
till.2015$Longitude = as.numeric(till.2015$Longitude)
till.2015$Latitude = as.numeric(till.2015$Latitude)
center_lon = median(till.2015$Longitude,na.rm = TRUE)
View(m1)
center_lat = median(till.2015$Latitude,na.rm = TRUE)
till.2015 %>% leaflet() %>%addProviderTiles("Esri.NatGeoWorldMap") %>%
addMarkers(lng = ~Longitude, lat = ~Latitude,clusterOptions = markerClusterOptions()) %>%
# controls
setView(lng=center_lon, lat=center_lat,zoom = 12)
names(xhist)[6] = "DateCreated"
Trend of 4311 calls
xhist %>%
mutate(year = format(DateCreated, format="%Y") )%>%
mutate(month = format(DateCreated, format="%m")) %>%
filter(!is.na(year)) %>%
filter(!is.na(month)) %>%
group_by(year,month) %>%
summarise(Count = n()) %>%
arrange(year,month) %>%
mutate(YearMonth = make_date(year=year,month=month) ) %>%
ggplot(aes(x=YearMonth,y=Count)) +
geom_line(size=1, color="red")+
geom_point(size=3, color="red") +
labs(x = 'Time', y = 'Count',title = 'Trend of 4311 Calls') +
theme_bw()

Newark4311TrendData = xhist %>%
mutate(year = format(DateCreated, format="%Y") ) %>%
mutate(month = format(DateCreated, format="%m")) %>%
filter(!is.na(year)) %>%
filter(!is.na(month)) %>%
group_by(year,month) %>%
summarise(Count = n()) %>%
arrange(year,month)
tsNewark4311TrendData = ts(Newark4311TrendData)
datatable((tsNewark4311TrendData), style="bootstrap", class="table-condensed", options = list(dom = 'tp',scrollX = TRUE))
fit <- auto.arima(tsNewark4311TrendData[,3])
preds = forecast(fit, h = 5)
preds %>% autoplot(include=40) +theme_bw()

till.2015 %>%
mutate(month = format(DateCreated, format="%m")) %>%
filter(!is.na(month)) %>%
group_by(month) %>%
summarise(Count = n()) %>%
arrange(desc(Count)) %>%
ungroup() %>%
mutate(month = reorder(month,Count)) %>%
ggplot(aes(x = month,y = Count)) +
geom_bar(stat='identity',colour="white", fill = fillColor2) +
geom_text(aes(x = month, y = 1, label = paste0("(",Count,")",sep="")),
hjust=0, vjust=.5, size = 4, colour = 'black',
fontface = 'bold') +
labs(x = 'Month',
y = 'Count',
title = 'Months with service requests counts') +
coord_flip() +
theme_bw()

x2018 %>%
mutate(year = format(DateCreated, format="%Y") )%>%
mutate(month = format(DateCreated, format="%m")) %>%
filter(!is.na(year)) %>%
filter(!is.na(month)) %>%
group_by(year,month) %>%
summarise(Count = n()) %>%
arrange(year,month) %>%
mutate(YearMonth = make_date(year=year,month=month) ) %>%
ggplot(aes(x=YearMonth,y=Count)) +
geom_line(size=1, color="red")+
geom_point(size=3, color="red") +
labs(x = 'Time', y = 'Count',title = 'Trend of 4311 Calls') +
theme_bw()
View(till.2015)

x2018 %>%
mutate(month = format(DateCreated, format="%m")) %>%
filter(!is.na(month)) %>%
group_by(month) %>%
summarise(Count = n()) %>%
arrange(desc(Count)) %>%
ungroup() %>%
mutate(month = reorder(month,Count)) %>%
ggplot(aes(x = month,y = Count)) +
geom_bar(stat='identity',colour="white", fill = fillColor2) +
geom_text(aes(x = month, y = 1, label = paste0("(",Count,")",sep="")),
hjust=0, vjust=.5, size = 4, colour = 'black',
fontface = 'bold') +
labs(x = 'Month',
y = 'Count',
title = 'Months with service requests counts') +
coord_flip() +
theme_bw()

na.omit(till.2015) %>%
group_by(Location) %>%
summarise(Count = length(Location)) %>%
arrange(desc(Count), .by_group = TRUE)
NA
Location Analisis of 2018
na.omit(x2018) %>%
group_by(Location) %>%
summarise(Count = length(Location)) %>%
arrange(desc(Count), .by_group = TRUE)
NA
Departmants in 2018
x2018 %>%
group_by(Typename,Department) %>%
summarise(Count = n()) %>%
ungroup() %>%
mutate(complaint_description = reorder(Department,Count)) %>%
arrange(desc(Count), .by_group = TRUE) %>%
head(50) %>%
ggplot(aes(x = reorder(Department,Count),y = Count)) +
geom_bar(stat='identity',colour="white", fill = fillColor) +
labs(x = 'Agencies',
y = 'Complaint Count',
title = 'Number of Comlpaints With Agency') +
coord_flip() +
theme_bw()

na.omit(x2018) %>%
group_by(Location,Typename) %>%
summarise(Count = length(Location)) %>%
arrange(desc(Count), .by_group = TRUE)
NA
na.omit(x2018) %>%
group_by(Location,Typename) %>%
summarise(Count = length(Typename)) %>%
arrange(desc(Count), .by_group = TRUE)
Newark4311SampleAll = till.2015 %>%
filter(!is.na(Latitude) ) %>%
filter(!is.na(Longitude))
leaflet() %>% addProviderTiles("Esri.NatGeoWorldMap") %>%
addCircles(data = Newark4311SampleAll,lng = ~Longitude, lat = ~Latitude,
color = ~c("red")) %>%
# controls
setView(lng=center_lon, lat=center_lat,zoom = 15)
library(leaflet.extras)
leaflet() %>% addProviderTiles("Esri.NatGeoWorldMap") %>%
setView(lng=center_lon, lat=center_lat,zoom = 17) %>%
addHeatmap( data = Newark4311SampleAll,
lng = ~Longitude, lat = ~Latitude,
blur = 20, max = 0.05, radius = 15
) %>%
addResetMapButton() %>%
addSearchGoogle() %>%
addFullscreenControl()
top10complaints = na.omit(till.2015) %>%
group_by(Complaint) %>%
summarise(Count = length(Location)) %>%
arrange(desc(Count), .by_group = TRUE)
#as.factor(till.2015$Complaint)
grepl(paste(except, collapse = "|"),ab)
[1] TRUE
write.csv(til.2015, file = "till2015.csv",row.names=FALSE)
Error in is.data.frame(x) : object 'til.2015' not found
names(till.2015)
[1] "Complaint" "ComplaintID" "Location" "Longitude" "Request Id" "DateCreated" "Latitude" "_id" "ID"
x2018$Latitude = geocoded$lat
x2018$Longitude = geocoded$long
Geocoded2018 %>%
filter(!is.na(lat) ) %>%
filter(!is.na(long)) %>%
leaflet() %>% addProviderTiles("Esri.NatGeoWorldMap") %>%
addMarkers(lng = ~long, lat = ~lat
,clusterOptions = markerClusterOptions()) %>%
# controls
setView(lng=center_lon, lat=center_lat,zoom = 15)
LS0tDQp0aXRsZTogIk5ld2FyayBDaXR5IDQzMTEiDQphdXRob3I6ICJBa2hpbCBQYXRpbCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCk5ld2FyayBDaXR5IDQzMTEgZGF0YSBhbmF5c2lzDQoNCmBgYHtyfQ0KIyBSZXF1aXJlZCBsaWJzDQpsaWJyYXJ5KGh0dHIpDQpsaWJyYXJ5KGpzb25saXRlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShsZWFmbGV0KQ0KI2luc3RhbGwucGFja2FnZXMoIkRUIikNCmxpYnJhcnkocnBhcnQpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQoNCmBgYA0KDQoNCg0KUGFyc2luZyBOZXdhcmsgSGlzdG9yaWMgZGF0YQ0KYGBge3J9DQoNCnVybHMuaGlzdCA9IGMoImh0dHA6Ly9kYXRhLmNpLm5ld2Fyay5uai51cy9tay9hcGkvMy9hY3Rpb24vZGF0YXN0b3JlX3NlYXJjaD9yZXNvdXJjZV9pZD0xY2ViY2Y0NC0zYjJjLTRmNjAtOWQ1ZC04MTdjYjM1YmY5ZGYmbGltaXQ9NDAwMDAiLA0KICAgICAgICAgICAgICAiaHR0cDovL2RhdGEuY2kubmV3YXJrLm5qLnVzL21rL2FwaS8zL2FjdGlvbi9kYXRhc3RvcmVfc2VhcmNoX3NxbD9zcWw9U0VMRUNUJTIwKiUyMGZyb20lMjAlMjIxY2ViY2Y0NC0zYjJjLTRmNjAtOWQ1ZC04MTdjYjM1YmY5ZGYlMjIlMjBXSEVSRSUyMF9pZCUyMCUzRSUyMDMxODg5IiwNCiAgICAgICAgICAgICAgImh0dHA6Ly9kYXRhLmNpLm5ld2Fyay5uai51cy9tay9hcGkvMy9hY3Rpb24vZGF0YXN0b3JlX3NlYXJjaF9zcWw/c3FsPVNFTEVDVCUyMColMjBmcm9tJTIwJTIyMWNlYmNmNDQtM2IyYy00ZjYwLTlkNWQtODE3Y2IzNWJmOWRmJTIyJTIwV0hFUkUlMjBfaWQlMjAlM0UlMjA2Mzg4OSIsDQogICAgICAgICAgICAgICJodHRwOi8vZGF0YS5jaS5uZXdhcmsubmoudXMvbWsvYXBpLzMvYWN0aW9uL2RhdGFzdG9yZV9zZWFyY2hfc3FsP3NxbD1TRUxFQ1QlMjAqJTIwZnJvbSUyMCUyMjFjZWJjZjQ0LTNiMmMtNGY2MC05ZDVkLTgxN2NiMzViZjlkZiUyMiUyMFdIRVJFJTIwX2lkJTIwJTNFJTIwOTU4ODgiKQ0KeGhpc3QgPSBkYXRhLmZyYW1lKCkNCg0KZm9yKGkgaW4gdXJscy5oaXN0ICl7DQogIHJhdy5yZXN1bHQgPC0gR0VUKHVybCA9IGkpDQogIHRoaXMucmF3LmNvbnRlbnQgPC0gcmF3VG9DaGFyKHJhdy5yZXN1bHQkY29udGVudCkNCiAgdGhpcy5jb250ZW50IDwtIGZyb21KU09OKHRoaXMucmF3LmNvbnRlbnQpDQogIGpzb25fZmlsZSA8LSBsYXBwbHkodGhpcy5jb250ZW50LCBmdW5jdGlvbih4KSB7DQogICAgeFtzYXBwbHkoeCwgaXMubnVsbCldIDwtIE5BDQogICAgdW5saXN0KHgpDQogIH0pDQogIA0KICB4aGlzdG9yaWMgPSB0aGlzLmNvbnRlbnQkcmVzdWx0JHJlY29yZHMNCiAgeGhpc3RvcmljID0geGhpc3RvcmljWyxjKCJUeXBlIE5hbWUiLCJUeXBlIElkIiwiU3RyZWV0IEFkZHJlc3MiICwgIkxvbmdpdHVkZSIsIlJlcXVlc3QgSWQiLCJSZXF1ZXN0IERhdGUiLCAiTGF0aXR1ZGUiICwgIl9pZCIgLCJJRCIpXQ0KICB4aGlzdCA9IHJiaW5kKHhoaXN0LHhoaXN0b3JpYykNCn0NCg0KZGltKHhoaXN0KQ0KYGBgDQoNCg0KUGFyc2luZyAyMDE1IERhdGENCmBgYHtyfQ0KDQp1cmwgPSAiaHR0cDovL2RhdGEuY2kubmV3YXJrLm5qLnVzL21rL2FwaS8zL2FjdGlvbi9kYXRhc3RvcmVfc2VhcmNoP3Jlc291cmNlX2lkPWJjOGUyOWViLTNjNTMtNDE4Yy1hNDJhLTRkYmJhYWU2NjhjMiZsaW1pdD02MDAwMCINCg0KcmF3LnJlc3VsdCA8LSBHRVQodXJsID0gdXJsKQ0KdGhpcy5yYXcuY29udGVudCA8LSByYXdUb0NoYXIocmF3LnJlc3VsdCRjb250ZW50KQ0KdGhpcy5jb250ZW50IDwtIGZyb21KU09OKHRoaXMucmF3LmNvbnRlbnQpDQoNCmpzb25fZmlsZSA8LSBsYXBwbHkodGhpcy5jb250ZW50LCBmdW5jdGlvbih4KSB7DQogIHhbc2FwcGx5KHgsIGlzLm51bGwpXSA8LSBOQQ0KICB1bmxpc3QoeCkNCn0pDQoNCngyMDE1ID0gdGhpcy5jb250ZW50JHJlc3VsdCRyZWNvcmRzDQpkaW0oeDIwMTUpDQoNCmBgYA0KDQoNClBhcnNpbmcgMjAxOCBEYXRhDQpgYGB7cn0NCg0KdXJscy4yMDE4ID0gYygiaHR0cDovL2RhdGEuY2kubmV3YXJrLm5qLnVzL21rL2FwaS8zL2FjdGlvbi9kYXRhc3RvcmVfc2VhcmNoP3Jlc291cmNlX2lkPWI5MjNjMWFkLTcyNDYtNDAwYi1hNTkzLTc4NWY2NzY3N2I5NCZsaW1pdD02ODQwOCIsDQogICAgICAgICAgICAgICJodHRwOi8vZGF0YS5jaS5uZXdhcmsubmoudXMvbWsvYXBpLzMvYWN0aW9uL2RhdGFzdG9yZV9zZWFyY2hfc3FsP3NxbD1TRUxFQ1QlMjAqJTIwZnJvbSUyMCUyMmI5MjNjMWFkLTcyNDYtNDAwYi1hNTkzLTc4NWY2NzY3N2I5NCUyMiUyMFdIRVJFJTIwX2lkJTIwJTNFJTIwMzE5OTMiLA0KICAgICAgICAgICAgICAiaHR0cDovL2RhdGEuY2kubmV3YXJrLm5qLnVzL21rL2FwaS8zL2FjdGlvbi9kYXRhc3RvcmVfc2VhcmNoX3NxbD9zcWw9U0VMRUNUJTIwKiUyMGZyb20lMjAlMjJiOTIzYzFhZC03MjQ2LTQwMGItYTU5My03ODVmNjc2NzdiOTQlMjIlMjBXSEVSRSUyMF9pZCUyMCUzRSUyMDY2NDk1IikNCngyMDE4ID0gZGF0YS5mcmFtZSgpDQoNCmZvcihpIGluIHVybHMuMjAxOCApew0KICByYXcucmVzdWx0IDwtIEdFVCh1cmwgPSBpKQ0KICB0aGlzLnJhdy5jb250ZW50IDwtIHJhd1RvQ2hhcihyYXcucmVzdWx0JGNvbnRlbnQpDQogIHRoaXMuY29udGVudCA8LSBmcm9tSlNPTih0aGlzLnJhdy5jb250ZW50KQ0KICBqc29uX2ZpbGUgPC0gbGFwcGx5KHRoaXMuY29udGVudCwgZnVuY3Rpb24oeCkgew0KICAgIHhbc2FwcGx5KHgsIGlzLm51bGwpXSA8LSBOQQ0KICAgIHVubGlzdCh4KQ0KICB9KQ0KICANCiAgeHRlbXAgPSB0aGlzLmNvbnRlbnQkcmVzdWx0JHJlY29yZHMNCiAgeHRlbXAgPSB4dGVtcFssYygiQ29tcGxhaW50IiwiRGF0ZUNyZWF0ZWQiLCJUeXBlbmFtZSIsIkxvY2F0aW9uIiwiTG90IiwgIkNvbXBsYWludElEIiwiRGVwYXJ0bWVudCIgLCJfaWQiICwiQmxvY2siKV0NCiAgeDIwMTggPSByYmluZCh4MjAxOCx4dGVtcCkNCn0NCg0KZGltKHgyMDE4KQ0KYGBgDQoNCmBgYHtyfQ0KbmFtZXMoeGhpc3QpDQpuYW1lcyh4MjAxNSkNCm5hbWVzKHgyMDE4KQ0KYGBgDQoNCg0KRml4aW5nIHRoZSAyMDE1IGRhdGFzZXQNCg0KYGBge3J9DQp4MjAxNSRgU3RyZWV0IEFkZHJlc3NgID0gcGFzdGUoeDIwMTUkYFN0cmVldCBOdW1iZXJgLHgyMDE1JGBTdHJlZXQgTmFtZWApDQoNCngyMDE1JGBTdHJlZXQgTmFtZWAgPSBOVUxMDQp4MjAxNSRgU3RyZWV0IE51bWJlcmAgPSBOVUxMDQpgYGANCg0KYGBge3J9DQpzdW0oaXMubmEoeDIwMTgpKQ0Kc3VtKGNvbXBsZXRlLmNhc2VzKHgyMDE4KSkNCmBgYA0KDQpBc3NpZ24gQmxhbmsgY2VsbHMgd2l0aCBOQSdzIGZvciB4MjAxNSANCmBgYHtyfQ0KeDIwMTUkYFN0cmVldCBBZGRyZXNzYFt4MjAxNSRgU3RyZWV0IEFkZHJlc3NgID09ICIgIl0gPSBOQQ0Kc3VtKGlzLm5hKHgyMDE1KSkNCmBgYA0KDQpBc3NpZ24gQmxhbmsgY2VsbHMgd2l0aCBOQSdzZm9yIHgyMDE4DQoNCmBgYHtyfQ0KeGhpc3RbeGhpc3QgPT0gIiAiXSA9IE5BDQpzdW0oaXMubmEoeGhpc3QpKQ0KYGBgDQoNClBhcnNlIERhdGUNCmBgYHtyfQ0KDQp4aGlzdCRgUmVxdWVzdCBEYXRlYCA9IGFzLlBPU0lYY3QoeGhpc3QkYFJlcXVlc3QgRGF0ZWApDQoNCngyMDE1JGBSZXF1ZXN0IERhdGVgID0gIjIwMTUiDQp4MjAxNSRgUmVxdWVzdCBEYXRlYCA9IGFzLkRhdGUoeDIwMTUkYFJlcXVlc3QgRGF0ZWAsZm9ybWF0ID0gIiVZIikNCg0KDQp4MjAxOCREYXRlQ3JlYXRlZCA9IGFzLlBPU0lYY3QoeDIwMTgkRGF0ZUNyZWF0ZWQsIGZvcm1hdCA9ICIlYiAlZCAlWSIpDQoNCmBgYA0KDQpDcmVhdGluZyBuZXcgY29tYmluZWQgZGF0YXNldCBmb3IgeDIwMTUgYW5kIHhoaXN0DQpgYGB7cn0NCnRpbGwuMjAxNSA9IHJiaW5kKHhoaXN0LHgyMDE1KQ0KYGBgDQoNCg0KYGBge3J9DQpuYW1lcyh0aWxsLjIwMTUpWzE6M108LWMoIkNvbXBsYWludCIsIkNvbXBsYWludElEIiwiTG9jYXRpb24iKQ0KbmFtZXModGlsbC4yMDE1KVs2XTwtYygiRGF0ZUNyZWF0ZWQiKQ0KDQpgYGANCg0Kb3ZlcmFsbDQzMTEgIERhdGFzZXQgd2l0aCBjb21tb24gYXR0cmlidXRlcw0KDQpgYGB7cn0NCm92ZXJhbGw0MzExID0gcmJpbmQodGlsbC4yMDE1WyxjKDEsMyw2KV0sIHgyMDE4WyxjKDEsNCwyKV0pDQpgYGANCg0KDQpgYGB7cn0NCmZpbGxDb2xvciA9ICIjRkZBMDdBIg0KZmlsbENvbG9yMiA9ICIjRjFDNDBGIg0KDQpgYGANCg0KYGBge3J9DQp4MjAxOCAlPiUNCiAgZ3JvdXBfYnkoVHlwZW5hbWUpICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShjb21wbGFpbnRfZGVzY3JpcHRpb24gPSByZW9yZGVyKFR5cGVuYW1lLENvdW50KSkgJT4lDQogIGFycmFuZ2UoZGVzYyhDb3VudCkpICU+JQ0KICBoZWFkKDEwKSAlPiUNCiAgDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoVHlwZW5hbWUsQ291bnQpLHkgPSBDb3VudCApKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jyxjb2xvdXI9IndoaXRlIiwgZmlsbCA9IGZpbGxDb2xvcjIpICsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gY29tcGxhaW50X2Rlc2NyaXB0aW9uLCB5ID0gMSwgbGFiZWwgPSBwYXN0ZTAoIigiLENvdW50LCIpIixzZXA9IiIpKSwNCiAgICAgICAgICAgIGhqdXN0PTAsIHZqdXN0PS41LCBzaXplID0gNCwgY29sb3VyID0gJ2JsYWNrJywNCiAgICAgICAgICAgIGZvbnRmYWNlID0gJ2JvbGQnKSArDQogIGxhYnMoeCA9ICdDb21wbGFpbnQgRGVzY3JpcHRvcnMnLCANCiAgICAgICB5ID0gJ0NvdW50JywgDQogICAgICAgdGl0bGUgPSAnSGlnaGVzdCBDb21wbGFpbnQgVHlwZW5hbWUgMjAxOCcpICsNCiAgY29vcmRfZmxpcCgpICsgDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCkNvbWJpbmcgdGhlIGRhdGFzZXRzIGZvciBnZXR0aW5nIG92ZXJhbGwgQ29tcGxhaW50IHR5cGVuYW1lDQpgYGB7cn0NCnRlbXAgPSBjKHhoaXN0JGBUeXBlIE5hbWVgLHgyMDE1JGBUeXBlIE5hbWVgLHgyMDE4JFR5cGVuYW1lKQ0KDQptMSA8LSBtYXRyaXgodGVtcCwgbmNvbD0xLCBieXJvdz1UUlVFKQ0Kb3ZlcmFsbC5jb21wbGFpbnRzIDwtIGFzLmRhdGEuZnJhbWUobTEsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpDQpvdmVyYWxsLmNvbXBsYWludHMNCmRpbShvdmVyYWxsLmNvbXBsYWludHMpDQpgYGANCmBgYHtyfQ0Kb3ZlcmFsbC5jb21wbGFpbnRzICU+JQ0KICBncm91cF9ieShWMSkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKGNvbXBsYWludF9kZXNjcmlwdGlvbiA9IHJlb3JkZXIoVjEsQ291bnQpKSAlPiUNCiAgYXJyYW5nZShkZXNjKENvdW50KSkgJT4lDQogIGhlYWQoMjApICU+JQ0KICANCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihWMSxDb3VudCkseSA9IENvdW50ICkpICsNCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLGNvbG91cj0id2hpdGUiLCBmaWxsID0gZmlsbENvbG9yMikgKw0KICBnZW9tX3RleHQoYWVzKHggPSBjb21wbGFpbnRfZGVzY3JpcHRpb24sIHkgPSAxLCBsYWJlbCA9IHBhc3RlMCgiKCIsQ291bnQsIikiLHNlcD0iIikpLA0KICAgICAgICAgICAgaGp1c3Q9MCwgdmp1c3Q9LjUsIHNpemUgPSA0LCBjb2xvdXIgPSAnYmxhY2snLA0KICAgICAgICAgICAgZm9udGZhY2UgPSAnYm9sZCcpICsNCiAgbGFicyh4ID0gJ0NvbXBsYWludCBEZXNjcmlwdG9ycycsIA0KICAgICAgIHkgPSAnQ291bnQnLCANCiAgICAgICB0aXRsZSA9ICdIaWdoZXN0IENvbXBsYWludCBUeXBlbmFtZScpICsNCiAgY29vcmRfZmxpcCgpICsgDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCkNsdXN0ZXIgQW5hbHlzaXMgb2YgQ29tcGxhaW50cyB0aHJvdWdoIExvY2F0aW9uDQoNCmBgYHtyfQ0KdGlsbC4yMDE1JExvbmdpdHVkZSA9IGFzLm51bWVyaWModGlsbC4yMDE1JExvbmdpdHVkZSkNCnRpbGwuMjAxNSRMYXRpdHVkZSA9ICBhcy5udW1lcmljKHRpbGwuMjAxNSRMYXRpdHVkZSkNCmNlbnRlcl9sb24gPSBtZWRpYW4odGlsbC4yMDE1JExvbmdpdHVkZSxuYS5ybSA9IFRSVUUpDQpjZW50ZXJfbGF0ID0gbWVkaWFuKHRpbGwuMjAxNSRMYXRpdHVkZSxuYS5ybSA9IFRSVUUpDQpgYGANCg0KDQpgYGB7cn0NCnRpbGwuMjAxNSAlPiUgbGVhZmxldCgpICU+JWFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuTmF0R2VvV29ybGRNYXAiKSAlPiUNCiAgDQogIGFkZE1hcmtlcnMobG5nID0gfkxvbmdpdHVkZSwgbGF0ID0gfkxhdGl0dWRlLGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSkgJT4lDQogICAgIyBjb250cm9scw0KICANCiAgc2V0Vmlldyhsbmc9Y2VudGVyX2xvbiwgbGF0PWNlbnRlcl9sYXQsem9vbSA9IDEyKSANCmBgYA0KDQpgYGB7cn0NCm5hbWVzKHhoaXN0KVs2XSA9ICJEYXRlQ3JlYXRlZCINCg0KYGBgDQoNClRyZW5kIG9mIDQzMTEgY2FsbHMNCmBgYHtyfQ0KDQp4aGlzdCAlPiUNCiAgbXV0YXRlKHllYXIgPSBmb3JtYXQoRGF0ZUNyZWF0ZWQsIGZvcm1hdD0iJVkiKSApJT4lDQogIG11dGF0ZShtb250aCA9IGZvcm1hdChEYXRlQ3JlYXRlZCwgZm9ybWF0PSIlbSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lDQogIGZpbHRlcighaXMubmEobW9udGgpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcixtb250aCkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoeWVhcixtb250aCkgJT4lDQogIG11dGF0ZShZZWFyTW9udGggPSBtYWtlX2RhdGUoeWVhcj15ZWFyLG1vbnRoPW1vbnRoKSApICU+JQ0KICANCg0KICBnZ3Bsb3QoYWVzKHg9WWVhck1vbnRoLHk9Q291bnQpKSArDQogIGdlb21fbGluZShzaXplPTEsIGNvbG9yPSJyZWQiKSsNCiAgZ2VvbV9wb2ludChzaXplPTMsIGNvbG9yPSJyZWQiKSArDQogIGxhYnMoeCA9ICdUaW1lJywgeSA9ICdDb3VudCcsdGl0bGUgPSAnVHJlbmQgb2YgNDMxMSBDYWxscycpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCmBgYHtyfQ0KTmV3YXJrNDMxMVRyZW5kRGF0YSA9IHhoaXN0ICU+JQ0KICBtdXRhdGUoeWVhciA9IGZvcm1hdChEYXRlQ3JlYXRlZCwgZm9ybWF0PSIlWSIpICkgJT4lDQogIG11dGF0ZShtb250aCA9IGZvcm1hdChEYXRlQ3JlYXRlZCwgZm9ybWF0PSIlbSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lDQogIGZpbHRlcighaXMubmEobW9udGgpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcixtb250aCkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoeWVhcixtb250aCkNCg0KdHNOZXdhcms0MzExVHJlbmREYXRhID0gdHMoTmV3YXJrNDMxMVRyZW5kRGF0YSkNCg0KZGF0YXRhYmxlKCh0c05ld2FyazQzMTFUcmVuZERhdGEpLCBzdHlsZT0iYm9vdHN0cmFwIiwgY2xhc3M9InRhYmxlLWNvbmRlbnNlZCIsIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICd0cCcsc2Nyb2xsWCA9IFRSVUUpKQ0KYGBgDQoNCmBgYHtyfQ0KZml0IDwtIGF1dG8uYXJpbWEodHNOZXdhcms0MzExVHJlbmREYXRhWywzXSkNCg0KcHJlZHMgPSBmb3JlY2FzdChmaXQsIGggPSA1KQ0KDQpwcmVkcyAlPiUgYXV0b3Bsb3QoaW5jbHVkZT00MCkgK3RoZW1lX2J3KCkNCmBgYA0KDQoNCg0KYGBge3J9DQp0aWxsLjIwMTUgJT4lDQogIG11dGF0ZShtb250aCA9IGZvcm1hdChEYXRlQ3JlYXRlZCwgZm9ybWF0PSIlbSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShtb250aCkpICU+JQ0KICBncm91cF9ieShtb250aCkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhDb3VudCkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShtb250aCA9IHJlb3JkZXIobW9udGgsQ291bnQpKSAlPiUNCiAgDQogICAgZ2dwbG90KGFlcyh4ID0gbW9udGgseSA9IENvdW50KSkgKw0KICAgIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jyxjb2xvdXI9IndoaXRlIiwgZmlsbCA9IGZpbGxDb2xvcjIpICsNCiAgICBnZW9tX3RleHQoYWVzKHggPSBtb250aCwgeSA9IDEsIGxhYmVsID0gcGFzdGUwKCIoIixDb3VudCwiKSIsc2VwPSIiKSksDQogICAgICAgICAgICAgIGhqdXN0PTAsIHZqdXN0PS41LCBzaXplID0gNCwgY29sb3VyID0gJ2JsYWNrJywNCiAgICAgICAgICAgICAgZm9udGZhY2UgPSAnYm9sZCcpICsNCiAgICBsYWJzKHggPSAnTW9udGgnLCANCiAgICAgICAgIHkgPSAnQ291bnQnLCANCiAgICAgICAgIHRpdGxlID0gJ01vbnRocyB3aXRoIHNlcnZpY2UgcmVxdWVzdHMgY291bnRzJykgKw0KICAgIGNvb3JkX2ZsaXAoKSArIA0KICAgIHRoZW1lX2J3KCkNCmBgYA0KDQoNCmBgYHtyfQ0KDQp4MjAxOCAlPiUNCiAgbXV0YXRlKHllYXIgPSBmb3JtYXQoRGF0ZUNyZWF0ZWQsIGZvcm1hdD0iJVkiKSApJT4lDQogIG11dGF0ZShtb250aCA9IGZvcm1hdChEYXRlQ3JlYXRlZCwgZm9ybWF0PSIlbSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lDQogIGZpbHRlcighaXMubmEobW9udGgpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcixtb250aCkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoeWVhcixtb250aCkgJT4lDQogIG11dGF0ZShZZWFyTW9udGggPSBtYWtlX2RhdGUoeWVhcj15ZWFyLG1vbnRoPW1vbnRoKSApICU+JQ0KICANCg0KICBnZ3Bsb3QoYWVzKHg9WWVhck1vbnRoLHk9Q291bnQpKSArDQogIGdlb21fbGluZShzaXplPTEsIGNvbG9yPSJyZWQiKSsNCiAgZ2VvbV9wb2ludChzaXplPTMsIGNvbG9yPSJyZWQiKSArDQogIGxhYnMoeCA9ICdUaW1lJywgeSA9ICdDb3VudCcsdGl0bGUgPSAnVHJlbmQgb2YgNDMxMSBDYWxscycpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCmBgYHtyfQ0KeDIwMTggJT4lDQogIG11dGF0ZShtb250aCA9IGZvcm1hdChEYXRlQ3JlYXRlZCwgZm9ybWF0PSIlbSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShtb250aCkpICU+JQ0KICBncm91cF9ieShtb250aCkgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhDb3VudCkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShtb250aCA9IHJlb3JkZXIobW9udGgsQ291bnQpKSAlPiUNCiAgDQogICAgZ2dwbG90KGFlcyh4ID0gbW9udGgseSA9IENvdW50KSkgKw0KICAgIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jyxjb2xvdXI9IndoaXRlIiwgZmlsbCA9IGZpbGxDb2xvcjIpICsNCiAgICBnZW9tX3RleHQoYWVzKHggPSBtb250aCwgeSA9IDEsIGxhYmVsID0gcGFzdGUwKCIoIixDb3VudCwiKSIsc2VwPSIiKSksDQogICAgICAgICAgICAgIGhqdXN0PTAsIHZqdXN0PS41LCBzaXplID0gNCwgY29sb3VyID0gJ2JsYWNrJywNCiAgICAgICAgICAgICAgZm9udGZhY2UgPSAnYm9sZCcpICsNCiAgICBsYWJzKHggPSAnTW9udGgnLCANCiAgICAgICAgIHkgPSAnQ291bnQnLCANCiAgICAgICAgIHRpdGxlID0gJ01vbnRocyB3aXRoIHNlcnZpY2UgcmVxdWVzdHMgY291bnRzJykgKw0KICAgIGNvb3JkX2ZsaXAoKSArIA0KICAgIHRoZW1lX2J3KCkNCmBgYA0KDQpgYGB7cn0NCm5hLm9taXQodGlsbC4yMDE1KSAlPiUNCiAgZ3JvdXBfYnkoTG9jYXRpb24pICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBsZW5ndGgoTG9jYXRpb24pKSAlPiUNCiAgYXJyYW5nZShkZXNjKENvdW50KSwgLmJ5X2dyb3VwID0gVFJVRSkNCiAgDQpgYGANCg0KTG9jYXRpb24gQW5hbGlzaXMgb2YgMjAxOA0KDQpgYGB7cn0NCm5hLm9taXQoeDIwMTgpICU+JQ0KICBncm91cF9ieShMb2NhdGlvbikgJT4lDQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChMb2NhdGlvbikpICU+JQ0KICBhcnJhbmdlKGRlc2MoQ291bnQpLCAuYnlfZ3JvdXAgPSBUUlVFKQ0KICANCmBgYA0KDQpEZXBhcnRtYW50cyBpbiAyMDE4DQpgYGB7cn0NCngyMDE4ICU+JQ0KICBncm91cF9ieShUeXBlbmFtZSxEZXBhcnRtZW50KSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoY29tcGxhaW50X2Rlc2NyaXB0aW9uID0gcmVvcmRlcihEZXBhcnRtZW50LENvdW50KSkgJT4lDQogIGFycmFuZ2UoZGVzYyhDb3VudCksIC5ieV9ncm91cCA9IFRSVUUpICU+JQ0KICBoZWFkKDUwKSAlPiUNCiAgDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoRGVwYXJ0bWVudCxDb3VudCkseSA9IENvdW50KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsY29sb3VyPSJ3aGl0ZSIsIGZpbGwgPSBmaWxsQ29sb3IpICsNCiAgbGFicyh4ID0gJ0FnZW5jaWVzJywgDQogICAgICAgeSA9ICdDb21wbGFpbnQgQ291bnQnLCANCiAgICAgICB0aXRsZSA9ICdOdW1iZXIgb2YgQ29tbHBhaW50cyBXaXRoIEFnZW5jeScpICsNCiAgY29vcmRfZmxpcCgpICsgDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCmBgYHtyfQ0KbmEub21pdCh4MjAxOCkgJT4lDQogIGdyb3VwX2J5KExvY2F0aW9uLFR5cGVuYW1lKSAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKExvY2F0aW9uKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhDb3VudCksIC5ieV9ncm91cCA9IFRSVUUpDQogDQpgYGANCg0KYGBge3J9DQpuYS5vbWl0KHgyMDE4KSAlPiUNCiAgZ3JvdXBfYnkoTG9jYXRpb24sVHlwZW5hbWUpICAlPiUNCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKFR5cGVuYW1lKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhDb3VudCksIC5ieV9ncm91cCA9IFRSVUUpDQoNCmBgYA0KDQpgYGB7cn0NCg0KTmV3YXJrNDMxMVNhbXBsZUFsbCA9IHRpbGwuMjAxNSAlPiUNCiAgZmlsdGVyKCFpcy5uYShMYXRpdHVkZSkgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShMb25naXR1ZGUpKSANCg0KbGVhZmxldCgpICU+JSBhZGRQcm92aWRlclRpbGVzKCJFc3JpLk5hdEdlb1dvcmxkTWFwIikgJT4lDQogIA0KYWRkQ2lyY2xlcyhkYXRhID0gTmV3YXJrNDMxMVNhbXBsZUFsbCxsbmcgPSB+TG9uZ2l0dWRlLCBsYXQgPSB+TGF0aXR1ZGUsIA0KICAgICAgICAgICBjb2xvciA9IH5jKCJyZWQiKSkgICU+JQ0KDQogICMgY29udHJvbHMNCiAgc2V0Vmlldyhsbmc9Y2VudGVyX2xvbiwgbGF0PWNlbnRlcl9sYXQsem9vbSA9IDE1KSANCg0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShsZWFmbGV0LmV4dHJhcykNCmxlYWZsZXQoKSAlPiUgYWRkUHJvdmlkZXJUaWxlcygiRXNyaS5OYXRHZW9Xb3JsZE1hcCIpICU+JQ0KICBzZXRWaWV3KGxuZz1jZW50ZXJfbG9uLCBsYXQ9Y2VudGVyX2xhdCx6b29tID0gMTcpICU+JQ0KICBhZGRIZWF0bWFwKCBkYXRhID0gTmV3YXJrNDMxMVNhbXBsZUFsbCwNCiAgICBsbmcgPSB+TG9uZ2l0dWRlLCBsYXQgPSB+TGF0aXR1ZGUsIA0KICAgIGJsdXIgPSAyMCwgbWF4ID0gMC4wNSwgcmFkaXVzID0gMTUNCikgJT4lDQogIGFkZFJlc2V0TWFwQnV0dG9uKCkgJT4lDQogIGFkZFNlYXJjaEdvb2dsZSgpICU+JQ0KICBhZGRGdWxsc2NyZWVuQ29udHJvbCgpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KdG9wMTBjb21wbGFpbnRzID0gbmEub21pdCh0aWxsLjIwMTUpICU+JQ0KICBncm91cF9ieShDb21wbGFpbnQpICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBsZW5ndGgoTG9jYXRpb24pKSAlPiUNCiAgYXJyYW5nZShkZXNjKENvdW50KSwgLmJ5X2dyb3VwID0gVFJVRSkNCiAgDQoNCmBgYA0KDQoNCmBgYHtyfQ0KI2FzLmZhY3Rvcih0aWxsLjIwMTUkQ29tcGxhaW50KQ0KYGBgDQoNCmBgYHtyfQ0KeDIwMTgkTG9jYXRpb24gJT4lIA0KICAgc3RyX3N1YnNldChwYXR0ZXJuID0gIl4qMTFUSCBBVkUqIikNCmFiID0gIjE2OSBBTEVYQU5ERVIgU1QsIE5ld2FyayxOZXcgSmVyc2V5IiANCmlmKHN0cl9zdWJzZXQocGF0dGVybiA9ICJeKkFMRVhBTkRFUioiKSl7DQogIHByaW50KCJPSyIpDQp9DQpleGNlcHQgPSBjKCJBTEVYQU5ERVIiLCIxMVRIIEFWRSIpDQpncmVwbChwYXN0ZShleGNlcHQsIGNvbGxhcHNlID0gInwiKSxhYikNCmBgYA0KDQogDQpgYGB7cn0NCiMgR2VvY29kaW5nIHNjcmlwdCBmb3IgbGFyZ2UgbGlzdCBvZiBhZGRyZXNzZXMuIA0KDQojIFNoYW5lIEx5bm4gMTAvMTAvMjAxMw0KI2luc3RhbGwucGFja2FnZXMoImdnbWFwIikNCg0KI2xvYWQgdXAgdGhlIGdnbWFwIGxpYnJhcnkNCmxpYnJhcnkoZ2dtYXApDQojIGdldCB0aGUgaW5wdXQgZGF0YQ0KaW5maWxlIDwtICJpbnB1dCINCmRhdGEgPC0geDIwMTgNCg0KIyBnZXQgdGhlIGFkZHJlc3MgbGlzdCwgYW5kIGFwcGVuZCAiSXJlbGFuZCIgdG8gdGhlIGVuZCB0byBpbmNyZWFzZSBhY2N1cmFjeSANCiMgKGNoYW5nZSBvciByZW1vdmUgdGhpcyBpZiB5b3VyIGFkZHJlc3MgYWxyZWFkeSBpbmNsdWRlIGEgY291bnRyeSBldGMuKQ0KYWRkcmVzc2VzID0gZGF0YSRMb2NhdGlvbg0KYWRkcmVzc2VzID0gcGFzdGUwKGFkZHJlc3NlcywgIiwgTmV3YXJrLE5ldyBKZXJzZXkiKQ0KDQpleGNlcHQgPSBjKCJBTEVYQU5ERVIiLCIxMVRIIEFWRSIsIk5PUldPT0QiLCJXRVNUIEVORCBBVkUiLCJTQUxFTSIsIlNUVVlWRVNBTlQiLCJDTElOVE9OIiwiUExFQVNBTlQiKQ0KDQoNCiNkZWZpbmUgYSBmdW5jdGlvbiB0aGF0IHdpbGwgcHJvY2VzcyBnb29nbGVzIHNlcnZlciByZXNwb25zZXMgZm9yIHVzLg0KZ2V0R2VvRGV0YWlscyA8LSBmdW5jdGlvbihhZGRyZXNzKXsgDQogIGlmKGdyZXBsKHBhc3RlKGV4Y2VwdCwgY29sbGFwc2UgPSAifCIpLGFkZHJlc3MpKXsNCiAgICByZXR1cm4oZGF0YS5mcmFtZShsYXQ9TkEsIGxvbmc9TkEsIGFjY3VyYWN5PU5BLCBmb3JtYXR0ZWRfYWRkcmVzcz1OQSwgYWRkcmVzc190eXBlPU5BLCBzdGF0dXM9TkEpDQogICApDQogIH0NCiAgICN1c2UgdGhlIGdlY29kZSBmdW5jdGlvbiB0byBxdWVyeSBnb29nbGUgc2VydmVycw0KICAgZ2VvX3JlcGx5ID0gZ2VvY29kZShhZGRyZXNzLCBvdXRwdXQ9J2FsbCcsIG1lc3NhZ2luZz1UUlVFLCBvdmVycmlkZV9saW1pdD1UUlVFKQ0KICAgI25vdyBleHRyYWN0IHRoZSBiaXRzIHRoYXQgd2UgbmVlZCBmcm9tIHRoZSByZXR1cm5lZCBsaXN0DQogICBhbnN3ZXIgPC0gZGF0YS5mcmFtZShsYXQ9TkEsIGxvbmc9TkEsIGFjY3VyYWN5PU5BLCBmb3JtYXR0ZWRfYWRkcmVzcz1OQSwgYWRkcmVzc190eXBlPU5BLCBzdGF0dXM9TkEpDQogICANCiAgIGFuc3dlciRzdGF0dXMgPC0gZ2VvX3JlcGx5JHN0YXR1cw0KDQogICAjaWYgd2UgYXJlIG92ZXIgdGhlIHF1ZXJ5IGxpbWl0IC0gd2FudCB0byBwYXVzZSBmb3IgYW4gaG91cg0KICAgd2hpbGUoZ2VvX3JlcGx5JHN0YXR1cyA9PSAiT1ZFUl9RVUVSWV9MSU1JVCIpew0KICAgICAgIHByaW50KCJPVkVSIFFVRVJZIExJTUlUIC0gUGF1c2luZyBmb3IgMSBob3VyIGF0OiIpIA0KICAgICAgIHRpbWUgPC0gU3lzLnRpbWUoKQ0KICAgICAgIHByaW50KGFzLmNoYXJhY3Rlcih0aW1lKSkNCiAgICAgICBTeXMuc2xlZXAoNjAqNjApDQogICAgICAgZ2VvX3JlcGx5ID0gZ2VvY29kZShhZGRyZXNzLCBvdXRwdXQ9J2FsbCcsIG1lc3NhZ2luZz1UUlVFLCBvdmVycmlkZV9saW1pdD1UUlVFKQ0KICAgICAgIGFuc3dlciRzdGF0dXMgPC0gZ2VvX3JlcGx5JHN0YXR1cw0KICAgfQ0KDQogICAjcmV0dXJuIE5hJ3MgaWYgd2UgZGlkbid0IGdldCBhIG1hdGNoOg0KICAgaWYgKGdlb19yZXBseSRzdGF0dXMgIT0gIk9LIil7DQogICAgICAgcmV0dXJuKGFuc3dlcikNCiAgIH0NCiAgICNlbHNlLCBleHRyYWN0IHdoYXQgd2UgbmVlZCBmcm9tIHRoZSBHb29nbGUgc2VydmVyIHJlcGx5IGludG8gYSBkYXRhZnJhbWU6DQogICBhbnN3ZXIkbGF0IDwtIGdlb19yZXBseSRyZXN1bHRzW1sxXV0kZ2VvbWV0cnkkbG9jYXRpb24kbGF0DQogICBhbnN3ZXIkbG9uZyA8LSBnZW9fcmVwbHkkcmVzdWx0c1tbMV1dJGdlb21ldHJ5JGxvY2F0aW9uJGxuZyAgIA0KICAgaWYgKGxlbmd0aChnZW9fcmVwbHkkcmVzdWx0c1tbMV1dJHR5cGVzKSA+IDApew0KICAgICAgIGFuc3dlciRhY2N1cmFjeSA8LSBnZW9fcmVwbHkkcmVzdWx0c1tbMV1dJHR5cGVzW1sxXV0NCiAgIH0NCiAgIGFuc3dlciRhZGRyZXNzX3R5cGUgPC0gcGFzdGUoZ2VvX3JlcGx5JHJlc3VsdHNbWzFdXSR0eXBlcywgY29sbGFwc2U9JywnKQ0KICAgYW5zd2VyJGZvcm1hdHRlZF9hZGRyZXNzIDwtIGdlb19yZXBseSRyZXN1bHRzW1sxXV0kZm9ybWF0dGVkX2FkZHJlc3MNCg0KICAgcmV0dXJuKGFuc3dlcikNCn0NCg0KI2luaXRpYWxpc2UgYSBkYXRhZnJhbWUgdG8gaG9sZCB0aGUgcmVzdWx0cw0KZ2VvY29kZWQgPC0gZGF0YS5mcmFtZSgpDQojIGZpbmQgb3V0IHdoZXJlIHRvIHN0YXJ0IGluIHRoZSBhZGRyZXNzIGxpc3QgKGlmIHRoZSBzY3JpcHQgd2FzIGludGVycnVwdGVkIGJlZm9yZSk6DQpzdGFydGluZGV4IDwtIDENCiNpZiBhIHRlbXAgZmlsZSBleGlzdHMgLSBsb2FkIGl0IHVwIGFuZCBjb3VudCB0aGUgcm93cyENCnRlbXBmaWxlbmFtZSA8LSBwYXN0ZTAoaW5maWxlLCAnX3RlbXBfZ2VvY29kZWQucmRzJykNCmlmIChmaWxlLmV4aXN0cyh0ZW1wZmlsZW5hbWUpKXsNCiAgICAgICBwcmludCgiRm91bmQgdGVtcCBmaWxlIC0gcmVzdW1pbmcgZnJvbSBpbmRleDoiKQ0KICAgICAgIGdlb2NvZGVkIDwtIHJlYWRSRFModGVtcGZpbGVuYW1lKQ0KICAgICAgIHN0YXJ0aW5kZXggPC0gbnJvdyhnZW9jb2RlZCkNCiAgICAgICBwcmludChzdGFydGluZGV4KQ0KfQ0KDQojIFN0YXJ0IHRoZSBnZW9jb2RpbmcgcHJvY2VzcyAtIGFkZHJlc3MgYnkgYWRkcmVzcy4gZ2VvY29kZSgpIGZ1bmN0aW9uIHRha2VzIGNhcmUgb2YgcXVlcnkgc3BlZWQgbGltaXQuDQpmb3IgKGlpIGluIHNlcShzdGFydGluZGV4LCBsZW5ndGgoYWRkcmVzc2VzKSkpew0KICAgcHJpbnQocGFzdGUoIldvcmtpbmcgb24gaW5kZXgiLCBpaSwgIm9mIiwgbGVuZ3RoKGFkZHJlc3NlcykpKQ0KICAgI3F1ZXJ5IHRoZSBnb29nbGUgZ2VvY29kZXIgLSB0aGlzIHdpbGwgcGF1c2UgaGVyZSBpZiB3ZSBhcmUgb3ZlciB0aGUgbGltaXQuDQogICByZXN1bHQgPSBnZXRHZW9EZXRhaWxzKGFkZHJlc3Nlc1tpaV0pIA0KICAgcHJpbnQocmVzdWx0JHN0YXR1cykgICAgIA0KICAgcmVzdWx0JGluZGV4IDwtIGlpDQogICAjYXBwZW5kIHRoZSBhbnN3ZXIgdG8gdGhlIHJlc3VsdHMgZmlsZS4NCiAgIGdlb2NvZGVkIDwtIHJiaW5kKGdlb2NvZGVkLCByZXN1bHQpDQogICAjc2F2ZSB0ZW1wb3JhcnkgcmVzdWx0cyBhcyB3ZSBhcmUgZ29pbmcgYWxvbmcNCiAgIHNhdmVSRFMoZ2VvY29kZWQsIHRlbXBmaWxlbmFtZSkNCn0NCg0KI25vdyB3ZSBhZGQgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdG8gdGhlIG1haW4gZGF0YQ0KZGF0YSRsYXQgPC0gZ2VvY29kZWQkbGF0DQpkYXRhJGxvbmcgPC0gZ2VvY29kZWQkbG9uZw0KZGF0YSRhY2N1cmFjeSA8LSBnZW9jb2RlZCRhY2N1cmFjeQ0KDQojZmluYWxseSB3cml0ZSBpdCBhbGwgdG8gdGhlIG91dHB1dCBmaWxlcw0Kc2F2ZVJEUyhkYXRhLCBwYXN0ZTAoIi4uL2RhdGEvIiwgaW5maWxlICwiX2dlb2NvZGVkLnJkcyIpKQ0Kd3JpdGUudGFibGUoZGF0YSwgZmlsZT1wYXN0ZTAoIi4uL2RhdGEvIiwgaW5maWxlICwiX2dlb2NvZGVkLmNzdiIpLCBzZXA9IiwiLCByb3cubmFtZXM9RkFMU0UpDQoNCmBgYA0KDQpgYGB7cn0NCmRpbShnZW9jb2RlZCkNCg0KZ2VvY29kZWQgPSBnZW9jb2RlZFstYyg1MjcpLF0NCg0Kd3JpdGUuY3N2KGdlb2NvZGVkLCBmaWxlID0gIkdlb2NvZGVkMjAxOC5jc3YiLHJvdy5uYW1lcz1GQUxTRSkNCg0Kd3JpdGUuY3N2KHgyMDE4LCBmaWxlID0gIngyMDE4LmNzdiIscm93Lm5hbWVzPUZBTFNFKQ0KDQoNCndyaXRlLmNzdih0aWxsLjIwMTUsIGZpbGUgPSAidGlsbDIwMTUuY3N2Iixyb3cubmFtZXM9RkFMU0UpDQoNCg0Kd3JpdGUuY3N2KHgyMDE1LCBmaWxlID0gIngyMDE1LmNzdiIscm93Lm5hbWVzPUZBTFNFKQ0KDQoNCndyaXRlLmNzdih4aGlzdCwgZmlsZSA9ICJ4aGlzdC5jc3YiLHJvdy5uYW1lcz1GQUxTRSkNCg0KDQp3cml0ZS5jc3Yob3ZlcmFsbDQzMTEsIGZpbGUgPSAib3ZlcmFsbDQzMTEuY3N2Iixyb3cubmFtZXM9RkFMU0UpDQoNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKHRpbGwuMjAxNSkNCngyMDE4JExhdGl0dWRlID0gZ2VvY29kZWQkbGF0DQoNCngyMDE4JExvbmdpdHVkZSA9IGdlb2NvZGVkJGxvbmcNCg0KYGBgDQoNCmBgYHtyfQ0KR2VvY29kZWQyMDE4ICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGxhdCkgKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShsb25nKSkgJT4lDQoNCmxlYWZsZXQoKSAlPiUgYWRkUHJvdmlkZXJUaWxlcygiRXNyaS5OYXRHZW9Xb3JsZE1hcCIpICU+JQ0KICANCmFkZE1hcmtlcnMobG5nID0gfmxvbmcsIGxhdCA9IH5sYXQNCiAgICAgICAgICAgLGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSkgICU+JQ0KDQogICMgY29udHJvbHMNCiAgc2V0Vmlldyhsbmc9Y2VudGVyX2xvbiwgbGF0PWNlbnRlcl9sYXQsem9vbSA9IDE1KSANCg0KYGBgDQoNCmBgYHtyfQ0KDQpgYGANCg0K